package com.baidu.mpks.backendweb.ktree.service;

import com.baidu.mpks.backendweb.file.dao.FileInfoDao;
import com.baidu.mpks.backendweb.file.service.FileAttachmentService;
import com.baidu.mpks.backendweb.file.service.FileRUDService;
import com.baidu.mpks.backendweb.file.service.FileUploadDownloadPreviewService;
import com.baidu.mpks.backendweb.ktree.dao.TreeNodeDao;
import com.baidu.mpks.backendweb.ktree.domain.BaseTreeNode;
import com.baidu.mpks.backendweb.ktree.domain.TreeNode;
import com.baidu.mpks.backendweb.ktree.dto.TreeNodeDto;
import com.baidu.mpks.backendweb.source.dao.SourceManageDao;
import com.baidu.mpks.backendweb.source.service.SourceService;
import com.baidu.mpks.backendweb.source.service.SourceStatisticService;
import com.baidu.mpks.backendweb.util.TreeUtil;
import com.baidu.mpks.backendweb.video.dao.VideoInfoDao;
import com.baidu.mpks.datasource.constant.SourceContentType;
import com.baidu.mpks.datasource.constant.SourceInputType;
import com.baidu.mpks.datasource.dao.DataSourceNodeIdRelationDao;
import com.baidu.mpks.datasource.dao.SourceDao;
import com.baidu.mpks.datasource.domain.Source;
import com.baidu.mpks.datasource.dto.SourceDto;
import com.baidu.mpks.exception.BusinessException;
import com.baidu.mpks.permission.dao.PermissionDataRoleDao;
import com.baidu.mpks.permission.dao.PermissionDataUserDao;
import com.baidu.mpks.permission.domain.PermissionData;
import com.baidu.mpks.permission.domain.PermissionDataRole;
import com.baidu.mpks.permission.service.PermissionCacheService;
import com.baidu.mpks.permission.service.PermissionHelper;
import com.baidu.mpks.permission.service.PermissionManageService;
import com.baidu.mpks.permission.service.PermissionService;
import com.baidu.mpks.role.dao.RoleDao;
import com.baidu.mpks.role.service.RoleService;
import com.baidu.mpks.user.dao.UserDao;
import com.baidu.mpks.user.service.UserService;
import com.baidu.mpks.util.SecurityUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;

@Service
@Slf4j
public class TreeServiceImpl implements TreeService {

    @Autowired
    private TreeNodeDao treeNodeDao;

    @Autowired
    @Lazy
    FileRUDService fileRUDService;

    @Autowired
    FileInfoDao fileInfoDao;

    @Autowired
    VideoInfoDao videoInfoDao;

    @Autowired
    SourceManageDao sourceManageDao;

    @Autowired
    SourceService sourceService;

    @Autowired
    SourceDao sourceDao;

    @Autowired
    DataSourceNodeIdRelationDao dataSourceNodeIdRelationDao;

    @Autowired
    @Lazy
    RoleDao roleDao;

    @Autowired
    UserDao userDao;

    @Autowired
    UserService userService;

    @Autowired
    PermissionService permissionService;

    @Autowired
    PermissionHelper permissionHelper;

    @Autowired
    PermissionDataRoleDao permissionDataRoleDao;

    @Autowired
    PermissionDataUserDao permissionDataUserDao;

    @Autowired
    PermissionCacheService permissionCacheService;

    @Autowired
    @Lazy
    PermissionManageService permissionManageService;

    @Autowired
    FileUploadDownloadPreviewService fileUploadDownloadPreviewService;

    @Autowired
    RoleService roleService;

    @Autowired
    SourceStatisticService sourceStatisticService;

    @Autowired
    @Lazy
    FileAttachmentService fileAttachmentService;

    @Autowired
    private SecurityUtil securityUtil;

    @Value("${api.token}")
    String apiToken;

    private Map<Integer, Set<Integer>> geneAncestorMap(List<TreeNode> tree) {
        if (org.apache.commons.collections.CollectionUtils.isEmpty(tree)) {
            return new HashMap<>();
        }
        Map<Integer, Set<Integer>> id2Parents = new HashMap<>();
        for (TreeNode treeNode : tree) {
            Set<Integer> parents = id2Parents.get(treeNode.getId());
            if (parents == null) {
                parents = new HashSet<>();
                id2Parents.put(treeNode.getId(), parents);
            }
            if (treeNode.getParentId() != 0 && treeNode.getParentId() >= 0) {
                parents.add(treeNode.getParentId());
            }
        }

        for (Integer id : id2Parents.keySet()) {
            Set<Integer> parents = id2Parents.get(id);
            Set<Integer> curparents = new HashSet<>();
            if (parents != null && !parents.isEmpty()) {
                while (curparents.size() != parents.size()) {
                    curparents.addAll(parents);
                    parents.clear();
                    for (Integer curId : curparents) {
                        parents.add(curId);
                        Set<Integer> temp = id2Parents.get(curId);
                        if (temp != null) {
                            parents.addAll(temp);
                        }
                    }
                }
                id2Parents.put(id, parents);
            }
        }

        return id2Parents;

    }

    /**
     * 暂时不设置权限
     */
    @Override
    public List<TreeNode> getTreeNodes() {
        return treeNodeDao.getTreeNodes();
    }

    @Override
    public List<TreeNode> getNodesByRoleId(Integer roleId) {
        List<TreeNode> result = new ArrayList<TreeNode>();
        List<TreeNode> allNodes = treeNodeDao.getTreeNodes();
        if (roleId.intValue() == 1) {
            // 如果是超级管理员，返回所有数据
            return allNodes;
        }

        List<String> propType = new ArrayList<>();
        propType.add(PermissionService.IDTYPE.SOURCE.getName());

        Set<PermissionDataRole> openData = permissionDataRoleDao.getOpenDataByIdTypes(propType);
        Map<String, Set<PermissionDataRole>> openDataMap = openData.stream()
                .collect(Collectors.groupingBy(PermissionDataRole::getIdType, Collectors.toSet()));

        // 过滤知识树节点的权限，key是OPType，value是知识树节点列表
        Map<String, Set<String>> sourceAuthInfo = new HashMap<>();
        List<Integer> roleIds = new ArrayList<>();
        roleIds.add(roleId);
        Map<String, List<PermissionData>> rolePermissionDataMap = permissionManageService
                .getRolePermissionDataMap(roleIds, securityUtil.getUserIdOrDefault(""));
        sourceAuthInfo = permissionManageService.setSourcePermission(rolePermissionDataMap, sourceAuthInfo,
                openDataMap.get(PermissionService.IDTYPE.SOURCE.getName()));
        Set<String> hasPermissionNodeList = sourceAuthInfo.get(PermissionService.OP.MANAGEREAD.getName());
        if (hasPermissionNodeList.size() > 0) {
            result = allNodes.stream().filter(item -> hasPermissionNodeList.contains(item.getId().toString()))
                    .collect(Collectors.toList());
        }

        return result;
    }

    @Override
    public Set<String> getParentsById(Integer nodeId) {
        List<TreeNode> allNodes = treeNodeDao.getTreeNodes();

        Map<Integer, Set<Integer>> parentNodesMap = this.geneAncestorMap(allNodes);
        Set<String> parentNodes = parentNodesMap.get(nodeId).stream().map(item -> item.toString())
                .collect(Collectors.toSet());
        return parentNodes;
    }

    @Override
    public TreeNodeDto getNodeInfoById(Integer nodeId) {
        TreeNode treeNode = treeNodeDao.getNodeById(nodeId);
        TreeNodeDto treeNodeDto = new TreeNodeDto();
        BeanUtils.copyProperties(treeNode, treeNodeDto);
        // TODO：后续如果产品侧需要会添加根据知识树节点获取权限信息
        treeNodeDto.setParentRoles(new HashSet<>());
        treeNodeDto.setSelfRoles(new HashSet<>());
        return treeNodeDto;
    }

    private Set<Integer> findAllNodeIdByAll(List<TreeNode> allNodes) {
        if (org.apache.commons.collections.CollectionUtils.isEmpty(allNodes)) {
            return new HashSet<>();
        }
        Set<Integer> allNodeIds = new HashSet<>();
        for (TreeNode treeNode : allNodes) {
            allNodeIds.add(treeNode.getId());
        }
        return allNodeIds;
    }

    private Set<Integer> geneChildrenMap(List<TreeNode> tree, Integer pid, Set<Integer> childMenu) {

        for (TreeNode treeNode : tree) {
            // 遍历出父id等于参数的id，add进子节点集合
            if (!treeNode.getParentId().equals(0) && treeNode.getParentId().equals(pid)) {
                // 递归遍历下一级
                geneChildrenMap(tree, treeNode.getId(), childMenu);
                childMenu.add(treeNode.getId());
            }
        }
        return childMenu;
    }

    private int getNodeLength(List<TreeNode> allNodes) {
        List<BaseTreeNode> tree = TreeUtil.buildTree(allNodes, 0);
        return TreeUtil.maxDepth(tree.get(0));
    }

    @Override
    public Set<Integer> getChildrensIncludeSelf(Integer nodeId) {
        List<TreeNode> allNodes = treeNodeDao.getTreeNodes();
        Set<Integer> childMenu = new CopyOnWriteArraySet<Integer>();
        Set<Integer> id2Childrens = geneChildrenMap(allNodes, nodeId, childMenu);

        // if (id2Childrens.isEmpty()) {
        id2Childrens.add(nodeId);
        // }

        return id2Childrens;
    }

    @Override
    public Set<Integer> getChildrensNotIncludeSelf(Integer nodeId) {
        List<TreeNode> allNodes = treeNodeDao.getTreeNodes();
        Set<Integer> childMenu = new CopyOnWriteArraySet<Integer>();
        Set<Integer> id2Childrens = geneChildrenMapNotIncludeSelf(allNodes, nodeId, childMenu);

        return id2Childrens;
    }

    private Set<Integer> geneChildrenMapNotIncludeSelf(List<TreeNode> tree, Integer pid, Set<Integer> childMenu) {

        for (TreeNode treeNode : tree) {
            // 遍历出父id等于参数的id，add进子节点集合
            if (treeNode.getParentId() != 0 && treeNode.getParentId() == pid) {
                // 递归遍历下一级
                geneChildrenMap(tree, treeNode.getId(), childMenu);
                childMenu.add(treeNode.getId());
            }
        }
        return childMenu;
    }

    /**
     * 用于知识树节点改名
     */
    @Override
    public void updateTree(TreeNode treeNode) {
        if (treeNode == null) {
            throw new BusinessException("参数异常，tree参数不能为空！");
        }

        treeNode.setName(treeNode.getName().trim());
        if (!StringUtils.isEmpty(treeNode.getName()) && treeNode.getParentId() != 0) {
            List<Integer> ids = treeNodeDao.getIdByNameParent(treeNode);
            if (!CollectionUtils.isEmpty(ids) && !ids.get(0).equals(treeNode.getId())) {
                throw new BusinessException("同一目录下存在相同名称！");
            }
            treeNodeDao.updateNameById(treeNode);
        }
        permissionCacheService.clearPermissionAll();
    }

    @Override
    @Transactional
    public TreeNode addTreeNode(TreeNode treeNode) {
        if (treeNode == null) {
            throw new BusinessException("增加树节点参数异常！");
        }

        treeNode.setName(treeNode.getName().trim());
        if (!StringUtils.isEmpty(treeNode.getName()) && treeNode.getParentId() != 0 && treeNode.getSort() != null) {
            Set<Integer> nodeIds = new HashSet<>();
            nodeIds.add(treeNode.getParentId());
            List<String> contentTypeList = new ArrayList<>();
            contentTypeList.add(SourceContentType.FILE.getName());
            contentTypeList.add(SourceContentType.VIDEO.getName());
            List<String> inputTypeList = SourceInputType.getAllNames();
            inputTypeList.remove(SourceInputType.UPLOAD.getName());
            List<Source> apiPushSources = sourceDao.findByNodeIdAndTypesPage(nodeIds, inputTypeList,
                    contentTypeList, 1, 0);
            if (CollectionUtils.isNotEmpty(apiPushSources)) {
                throw new BusinessException("该节点下api推送类资源，不能添加子节点！");
            }
            List<Integer> sources =
                    sourceDao.findSourceIdByNodeIdsAndTypes(nodeIds, SourceInputType.UPLOAD.getName(), contentTypeList);
            Long count = sourceService.countMongoBySourceId(contentTypeList, sources);
            if (count > 0) {
                throw new BusinessException("该节点下有文档和视频资源，不能添加子节点！");
            }
            List<Integer> ids = treeNodeDao.getIdByNameParent(treeNode);
            if (!CollectionUtils.isEmpty(ids)) {
                throw new BusinessException("同一目录下存在相同名称！");
            }

            treeNodeDao.addNode(treeNode);

            // 创建手动上传的资源
            Source fileSource = new Source();
            fileSource.setName(SourceInputType.UPLOAD.getDesc());
            fileSource.setContent("");
            fileSource.setContentType(SourceContentType.FILE.getName());
            fileSource.setInputType(SourceInputType.UPLOAD.getName());
            fileSource.setNodeId(treeNode.getId());
            fileSource.setCreateBy(securityUtil.getUserIdOrDefault(""));
            fileSource.setUpdateBy(securityUtil.getUserIdOrDefault(""));
            fileSource.setContent(sourceService.getToken());
            try {
                sourceService.addSource(fileSource);
            } catch (IOException e) {
                treeNodeDao.deleteById(treeNode.getId());
                throw new BusinessException("添加资源失败，创建节点失败");
            }

            Source videoSource = new Source();
            try {
                BeanUtils.copyProperties(fileSource, videoSource);
                videoSource.setContentType(SourceContentType.VIDEO.getName());
                sourceService.addSource(videoSource);
            } catch (IOException e) {
                treeNodeDao.deleteById(treeNode.getId());
                throw new BusinessException("添加资源失败，创建节点失败");
            }

        } else {
            throw new BusinessException("树信息不可以为空！");
        }

        permissionCacheService.clearPermissionAll();
        return treeNode;
    }

    // @Override
    // public void updateTreePermission(Integer nodeId, Set<Integer> roleIds) {

    //     Set<Integer> oldRoleIds = permissionDataRoleDao.getRoleIdsByDataIdAndIdType(nodeId.toString(),
    //     PermissionService.IDTYPE.SOURCE.getName());

    //     this.deleteTreeRolePermission(nodeId, oldRoleIds);
    //     this.addTreePermission(nodeId, roleIds);

    //     permissionCacheService.clearPermissionAll();

    // }

    // @Override
    // public void deleteTreePermission(Integer nodeId, Set<Integer> roleIds) {
    //     Set<Integer> nodeIds = this.getChildrensIncludeSelf(nodeId);
    //     Set<String> nodeIdsStr = nodeIds.stream().map(item -> item.toString()).collect(Collectors.toSet());
    //     permissionService.deletePermissionByDataIdsAndRoleIdsAndIdType(nodeIdsStr, roleIds, PermissionService
    //     .IDTYPE.SOURCE.getName());
    //     permissionCacheService.clearPermissionAll();
    // }
    // @Override
    // public void deleteTreeRolePermission(Integer nodeId, Set<Integer> roleIds) {
    //     Set<Integer> nodeIds = this.getChildrensIncludeSelf(nodeId);
    //     Set<String> nodeIdsStr = nodeIds.stream().map(item -> item.toString()).collect(Collectors.toSet());
    //     permissionService.deleteRolePermissionByDataIdsAndRoleIdsAndIdType(nodeIdsStr, roleIds, PermissionService
    //     .IDTYPE.SOURCE.getName());
    //     permissionCacheService.clearPermissionAll();
    // }

    // @Override
    // public void addTreePermission(Integer nodeId, Set<Integer> roleIds) {
    //     Set<Integer> nodeIds = this.getChildrensIncludeSelf(nodeId);
    //     for (Integer id : nodeIds) {
    //         AuthInfo authInfo = new AuthInfo();
    //         authInfo.setDataId(id.toString());
    //         authInfo.setIdType(PermissionService.IDTYPE.SOURCE.getName());
    //         authInfo.setRoles(roleIds);
    //         authInfo.setOp(PermissionService.OP.READ.getName());
    //         authInfo.setUpdateBy(securityUtil.getUserIdOrDefault(""));
    //         permissionService.addDataPermissionByDataId(authInfo);
    //     }

    //     permissionCacheService.clearPermissionAll();
    // }

    @Override
    @Transactional
    public void delete(int nodeId) {
        // 当前业务要求只删除叶子结点，不允许删除非叶子结点，不必级联删除
        if (treeNodeDao.countByParendId(nodeId) > 0) {
            throw new BusinessException("非叶子节点不可以删除！");
        }

        // 节点下有资源不允许删除
        Set<Integer> nodeIds = this.getChildrensIncludeSelf(nodeId);
        List<Source> sources = sourceDao.findByNodeIds(nodeIds);
        List<Source> excludeUploadSource = new ArrayList<>();
        List<Integer> uploadSourceIds = new ArrayList<>();
        sources.forEach(source -> {
            if (!source.getInputType().equals(SourceInputType.UPLOAD.getName())) {
                excludeUploadSource.add(source);
            } else {
                uploadSourceIds.add(source.getId());
            }
        });
        if (CollectionUtils.isNotEmpty(excludeUploadSource)) {
            throw new BusinessException("节点下有资源，不允许删除");
        }
        if (CollectionUtils.isNotEmpty(fileInfoDao.findBySourceIds(uploadSourceIds)) ||
                CollectionUtils.isNotEmpty(videoInfoDao.listBySourceIds(uploadSourceIds))) {
            throw new BusinessException("节点下有内容，不允许删除");
        }

        List<Integer> dataSourceList = dataSourceNodeIdRelationDao.getByNodeId(nodeId);
        if (dataSourceList.size() > 0) {
            throw new BusinessException("节点已绑定数据源，不允许删除，请先在数据源中删除知识树节点信息");
        }

        treeNodeDao.deleteById(nodeId);

        sourceManageDao.deleteSourceByNodeId(nodeId);
        permissionCacheService.clearPermissionAll();
        // Set<Integer> roleIds = permissionDataRoleDao.getRoleIdsByDataIdAndIdType(nodeId + "", PermissionService
        // .IDTYPE.SOURCE.getName());
        // this.deleteTreePermission(nodeId, roleIds);
    }

    @Override
    public Map<Integer, String> getNodesMap() {
        List<TreeNode> nodes = treeNodeDao.getTreeNodes();
        Map<Integer, String> nodesMap = nodes.stream().collect(Collectors.toMap(TreeNode::getId, TreeNode::getName));
        return nodesMap;
    }

    @Override
    public Map<String, Object> getTotalByNodeIdAndContentType(Integer nodeId, String contentType) {
        String pattern = "yyyy-MM-dd HH:mm:ss";
        DateFormat df = new SimpleDateFormat(pattern);
        Map<String, Object> nodeIdMap = new HashMap<>();
        nodeIdMap.put("id", nodeId);
        if (treeNodeDao.getNodeById(nodeId) != null) {
            nodeIdMap.put("updateTime", df.format(treeNodeDao.getNodeById(nodeId).getUpdateTime()));
        }
        // 查询每个知识树节点下，相关资源的总数
        long total = sourceStatisticService.getNodeTotalByNodeId(nodeId, contentType);
        nodeIdMap.put("total", total);
        return nodeIdMap;
    }

    @Override
    public List<SourceDto> getSourceListByNodeId(Integer nodeId) {
        if (nodeId == null) {
            return Collections.EMPTY_LIST;
        }
        Set<Integer> nodeIds = getChildrensIncludeSelf(nodeId);
        if (CollectionUtils.isEmpty(nodeIds)) {
            return Collections.EMPTY_LIST;
        }
        List<Source> sources = sourceDao.findByNodeIds(nodeIds);
        List<SourceDto> sourceDtoS = sources.stream().map(source -> {
            return SourceDto.builder().id(source.getId()).contentType(source.getContentType()).name(source.getName())
                    .build();
        }).collect(Collectors.toList());
        return sourceDtoS;
    }

    @Override
    public Set<Integer> getChildPermittedNode(Integer nodeId) {
        String userIdOrDefault = securityUtil.getUserIdOrDefault("001");
        try {
            Map<PermissionService.OP, PermissionHelper.Info> allPermission =
                    permissionHelper.getPermissionCachedByUserId(userIdOrDefault);
            Set<String> set = allPermission.get(PermissionService.OP.MANAGEREAD).getSourceAuthInfo();
            Set<Integer> nodeIdSet = set.stream().map(Integer::valueOf).collect(Collectors.toSet());
            Set<Integer> childrenIncludeSelf = this.getChildrensIncludeSelf(nodeId);
            return new HashSet(CollectionUtils.intersection(nodeIdSet, childrenIncludeSelf));
        } catch (IOException e) {
            log.error("cannot get right permission for user: {}， e: {}", userIdOrDefault, e);
            throw new BusinessException(1003, "获取用户权限信息失败");
        }
    }

    public List<TreeNode> getAllParent(Integer nodeId) {
        if (nodeId == null) {
            throw new BusinessException(400, "nodeId不能为null");
        }
        List<TreeNode> nodes = treeNodeDao.getTreeNodes();

        if (CollectionUtils.isEmpty(nodes)) {
            log.info("nodes is null");
            return nodes;
        }
        Map<Integer, TreeNode> idToNode = nodes.stream().collect(Collectors.toMap(TreeNode::getId, node -> node));
        List<TreeNode> treeNodes = new ArrayList<>();
        Integer parentId = nodeId;
        // 防止死循环
        for (int i = 0; i < nodes.size(); i++) {
            if (parentId == null) {
                break;
            }
            TreeNode treeNode = idToNode.get(parentId);
            if (treeNode == null) {
                break;
            }
            treeNodes.add(treeNode);
            parentId = treeNode.getParentId();
        }
        if (CollectionUtils.isEmpty(treeNodes)) {
            return treeNodes;
        }
        List<TreeNode> result = new ArrayList<>();
        // 反转链表
        for (int i = treeNodes.size() - 1; i >= 0; i--) {
            result.add(treeNodes.get(i));
        }
        return result;
    }
}
